<?php

class Upload {

    private $tamanhoArquivo;
    private $largura;
    private $altura;
    private $tipoArquivo;
    private $tiposDeArquivos;
    private $diretorio;
    public $arquivo;
    private $erro;
    private $msg;

    /**
     * Define o nome do(s) campo(s) do tipo file do formulario
     *
     * @param string $nomeCampo Nome do campo que vai ser recebido
     * @param array $nomeCampo Nome do campo que vai ser recebido
     */
    public function __construct($campo) {
        $nomeCampo = array();
        if (is_string($campo))
            $nomeCampo[] = $campo;
        else if (is_array($campo))
            $nomeCampo = $campo;

        $i = 0;
        $dadosArquivo = array();
        $this->arquivo = array();

        error_reporting(E_ERROR | E_WARNING | E_PARSE);
        
        while (isset($_FILES["{$nomeCampo[$i]}"]['error']) && $_FILES["{$nomeCampo[$i]}"]['error'] == UPLOAD_ERR_OK) {
            $dadosArquivo[$nomeCampo[$i]]['name'] = $_FILES[$nomeCampo[$i]]['name'];
            $dadosArquivo[$nomeCampo[$i]]['type'] = $_FILES[$nomeCampo[$i]]['type'];
            $dadosArquivo[$nomeCampo[$i]]['error'] = $_FILES[$nomeCampo[$i]]['error'];
            $dadosArquivo[$nomeCampo[$i]]['size'] = $_FILES[$nomeCampo[$i]]['size'];
            $dadosArquivo[$nomeCampo[$i]]['tmp_name'] = $_FILES[$nomeCampo[$i]]['tmp_name'];
            $dadosArquivo[$nomeCampo[$i]]['nome_tratado'] = $this->renomeiaArquivo($_FILES[$nomeCampo[$i]]['name']);
            $dadosArquivo[$nomeCampo[$i]]['erro'] = array();
            $dadosArquivo[$nomeCampo[$i]]['status'] = 'nao_enviado';
            $this->arquivo = $dadosArquivo;
            $i++;
        }
    }

    /**
     * Remove acentos, espacos e caracteres especiais
     * @param string $string Nome do arquivo
     */
    private function renomeiaArquivo($nome) {
        $nome = strtolower($nome);
        /*
         * A funcaoo "strtr" substitui os caracteres acentuados pelos nao acentuados.
         * A funcaoo "ereg_replace" utiliza uma expressao regular que remove todos os
         * caracteres que nao sao letras, numeros e sao diferentes de "_" (underscore).
         */
        $nome = @ereg_replace("[^a-zA-Z0-9_.]", "", strtr($nome, "Ã¡Ã Ã£Ã¢Ã¤Ã©Ã¨ÃªÃ«Ã­Ã¬Ã®Ã¯Ã³Ã²ÃµÃ´Ã¶ÃºÃ¹Ã»Ã¼Ã§Ã±. ", "aaaaaeeeeiiiiooooouuuucn._"));

        return $nome;
    }

    /**
     * Metodo responsavel em executa o upload
     *
     * @param string $diretorio caminho onde sera salvo o arquivo
     * @param bool $substituir indica se o arquivo pode substituir outro
     * arquivo com o mesmo nome por padrao nao podera
     * @return Ira retornar um array com as informacoes dos arquivos
     */
    public function upload($diretorio, $substituir = false) {

        // Seta o limite de execucao em zero para evitar erros
        set_time_limit(0);

        if (!$this->verificaDiretorio($diretorio)) {
            return false;
        }

        $this->verificaTipoArquivo();
        $this->verificaTamanhoArquivo();
        $this->verificaDimencoes();
        
        foreach ($this->arquivo as $campo => $arquivo) {
            if (count($arquivo['erro']) == 0) {
                if ($substituir) {
                    if (move_uploaded_file($arquivo['tmp_name'], "{$diretorio}{$arquivo['nome_tratado']}")) {
                        $this->arquivo[$campo]['status'] = 'enviado';
                    } else {
                        $this->arquivo[$campo]['erro'][] = 'enviar_arquivo';
                    }
                } else {
                    if (file_exists("{$diretorio}{$arquivo['nome_tratado']}")) {
                        $this->arquivo[$campo]['erro'][] = 'arquivo_existe';
                    } else {
                        if (move_uploaded_file($arquivo['tmp_name'], "{$diretorio}{$arquivo['nome_tratado']}")) {
                            $this->arquivo[$campo]['status'] = 'enviado';
                        } else {
                            $this->arquivo[$campo]['erro'][] = 'enviar_arquivo';
                        }
                    }
                }
            }
        }

        foreach ($this->arquivo as $campo => $arquivo) {
            if ($arquivo['status'] == 'enviado') {
                $this->msg[$campo] = "Arquivo \"{$arquivo['name']}\" Foi enviado com sucesso";
                $this->msg['status'] = "enviado";
                $this->msg['nome_arquivo_tratado'] = $arquivo['nome_tratado'];
            } else {
                $msgErro = array();
                $msgErro[] = "O arquivo \'{$arquivo['name']}\' nao pode ser enviado devido ao(s) seguinte(s) problema(s):";
                if (isset($arquivo['erro'])) {
                    if (is_array($arquivo['erro'])) {
                        foreach ($arquivo['erro'] as $erro) {
                            $msgErro[] = $this->getErro($erro);
                        }
                    } else {
                        $msgErro[] = $this->getErro($arquivo['erro']);
                    }
                }
                $this->msg[$campo] = $msgErro;
                $this->msg['status'] = "erro";
            }
        }
        return $this->msg;
    }

    /**
     * Metodo responsavem em setar o nome dos arquivos
     *
     * @param string $campo Onome do campo no formulario
     * @param string $nome O nome do arquivo
     */
    public function setNomeArquivo($campo, $nome) {
        if (isset($this->arquivo[$campo])) {
            preg_match('/(.+)\.([^.]{1,10})$/', $this->arquivo[$campo]['nome_tratado'], $extencao);
            $this->arquivo[$campo]['nome_tratado'] = "{$nome}.{$extencao[2]}";
        }
    }

    /**
     * Metodo responsavem em retornar o nome do arquivo
     *
     * @param string $campo Onome do campo no formulario
     * @return string Retorna o nome do arquivo
     */
    public function getNomeArquivo($campo) {
        if (isset($this->arquivo[$campo])) {
            return $this->arquivo[$campo]['nome_tratado'];
        }
    }

    /**
     * Metodo responsavem em excluir um determinado arquivo
     *
     * @param string $campo Nome do campo no formulario
     */
    public function removeArquivo($campo) {
        if (isset($this->arquivo[$campo])) {
            if (is_file("{$this->diretorio}{$this->arquivo[$campo]['nome_tratado']}")) {
                if (unlink("{$this->diretorio}{$this->arquivo[$campo]['nome_tratado']}")) {
                    $this->arquivo[$campo]['status'] = 'excluido';
                }
            }
        }
    }

    /**
     * Metodo responsavel em setar a altura maxima de um arquivo (imagem)
     *
     * @param integer $altura A altura em pixels
     */
    public function setAltura($altura) {
        $this->altura = $altura;
    }

    /**
     * Metodo responsavel em setar a largura maxima de um arquivo (imagem)
     *
     * @param integer $largura A Largura da imagem em pixels
     */
    public function setLargura($largura) {
        $this->largura = $largura;
    }

    /**
     * Metodo responsavel em verificar dimensoes do arquivo
     */
    private function verificaDimencoes() {
        if ($this->tipoArquivo) {
            foreach ($this->arquivo as $campo => $arquivo) {
                $tamanhos = getimagesize($arquivo["tmp_name"]);
                if ($this->largura) {
                    if ($tamanhos[0] > $this->largura) {
                        $this->arquivo[$campo]['erro'][] = 'largura';
                    }
                }
                if ($this->altura) {
                    if ($tamanhos[1] > $this->altura) {
                        $this->arquivo[$campo]['erro'][] = 'altura';
                    }
                }
            }
        }
    }

    /**
     * Metodo responsavel em setar o tamanho maximo do arquivo em bytes
     *
     * @param integer $tamanho
     */
    public function setTamanhoArquivo($tamanho) {
        $this->tamanhoArquivo = $tamanho;
    }

    /**
     * Metodo responsavel em verificar o tamanho do arquivo
     */
    private function verificaTamanhoArquivo() {
        // Verifica tamanho do arquivo
        if ($this->tamanhoArquivo) {
            foreach ($this->arquivo as $campo => $arquivo) {
                if ($arquivo["size"] > $this->tamanhoArquivo) {
                    $this->arquivo[$campo]['erro'][] = 'tamanho';
                }
            }
        }
    }

    /**
     * Metodo responsavel em setar o tipo de arquivo
     *
     * @param string $arquivo
     */
    public function setTipoArquivo($arquivo, $tipos = null) {
        switch ($arquivo) {
            case 'compactado':
                $this->tipoArquivo = 'imagem';
                $this->tiposDeArquivos = '^application\/(octet-stream)$';
                break;
            case 'imagem':
                $this->tipoArquivo = 'imagem';
                $this->tiposDeArquivos = '^image\/(pjpeg|jpeg|jpg|gif|png|bmp)$';
                break;
            case 'pdf':
                $this->tipoArquivo = 'pdf';
                $this->tiposDeArquivos = '^application\/(pdf)$';
                break;
            case 'logotipo':
                $this->tipoArquivo = 'logotipo';
                $this->tiposDeArquivos = '^(image|application)\/(pjpeg|jpeg|jpg|pdf|tif|tiff|cdr|dwg|zip|octet-stream|force-download)$';
                break;
            case 'excel':
                $this->tipoArquivo = 'excel';
                $this->tiposDeArquivos = '^application\/(vnd.ms-excel|vnd.openxmlformats-officedocument.spreadsheetml.sheet|msexcel)$';
                break;
            case 'definido':
                $this->tipoArquivo = 'definido';
                $this->tiposDeArquivos = "^(image|application)\/({$tipos}|octet-stream)$";
                break;
            case 'CNAB':
                $this->tipoArquivo = 'CNAB';
                $this->tiposDeArquivos = '^(text|application)\/(plain|octet-stream)$';
                /* extensoes de arquivos CNAB -- cpe|rem|cpr|2pe|2re|2pr|2rr|pag|ret */
                break;
        }
    }

    /**
     * Metodo responsavel em verificar o tipo de arquivo
     */
    public function verificaTipoArquivo() {
        if ($this->tipoArquivo) {
            foreach ($this->arquivo as $campo => $arquivo) {
                if (!@eregi($this->tiposDeArquivos, $arquivo["type"])) {
                    $this->arquivo[$campo]['erro'][] = 'formato_invalido';
                }
            }
        }
    }

    /**
     * Metodo responsavel em verificar se diretorio e valido
     * caso nao seja tenta cria-lo
     *
     * @param string $diretorio Caminho do diretorio
     * @return bool
     */
    private function verificaDiretorio($diretorio) {
        // Verifica se o diretorio passado como parametro existe
        try {
            if (!is_dir($diretorio)) {
                if (mkdir($diretorio, 0777, true)) {
                    $this->diretorio = $diretorio;
                    return true;
                } else {
                    $this->erro[] = "Diretorio nao existe e nao pode ser criado";
                    return false;
                }
            } else {
                $this->diretorio = $diretorio;
                return true;
            }
        } catch (Exception $e) {
            echo "<pre>";
            print_r($e);
            echo "</pre>";
        }
    }

    /**
     * Metodo responsavel em retornar as mensagens de erro.
     * @param string $erro nome do erro
     * @return string Ira retornar a mensagem de erro completa
     */
    public function getErro($erro) {
        switch ($erro) {
            case 'formato_invalido':
                return 'Arquivo em formato inv&aacute;lido.';
                break;
            case 'tamanho':
                return "Arquivo em tamanho muito grande! O arquivo deve ter no m&aacute;ximo {$this->tamanhoArquivo} bytes.";
                break;
            case 'largura':
                return "Largura da imagem n&atilde;o deve ultrapassar {$this->largura} pixels";
                break;
            case 'altura':
                return "Altura da imagem n&atilde;o deve ultrapassar {$this->altura} pixels";
                break;
            case 'arquivo_existe':
                return "J&aacute; existe um arquivo com este nome";
                break;
            case 'enviar_arquivo':
                return "Erro ao enviar arquivo";
                break;
        }
    }

}

?>